#define _CRT_SECURE_NO_WARNINGS	
#define _CRT_NONSTDC_NO_WARNINGS
#include <conio.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <io.h>
#include <math.h>
#define Up 0
#define Dn 1
#define Lt 2
#define Rt 3

void hideCursor()
{
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(hOut, &cursorInfo);
    cursorInfo.bVisible = FALSE; // 隐藏光标
    SetConsoleCursorInfo(hOut, &cursorInfo);
}

void ClearScreen() 
{
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = {0, 0};
    DWORD count;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    GetConsoleScreenBufferInfo(hStdOut, &csbi);
    FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
    SetConsoleCursorPosition(hStdOut, coord);
}

void SpaceFrame( void )
{
	for( int i = 0; i < 30; i ++ )
	{
		for( int j = 0; j < 80; j ++ )
		{
			printf( " " );
		}
		printf( "\u2588\u2588\n" );
	}
	for( int i = 0; i < 82; i ++ )
		printf( "\u2588" );
	printf( "\n" );
}

void setCursorPos(int x, int y) 
{
	COORD coord;
	coord.X = x;
	coord.Y = y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void MiSleep(int microseconds) {
    LARGE_INTEGER freq, start, end;
    QueryPerformanceFrequency(&freq);  
    QueryPerformanceCounter(&start);   

    long long target = start.QuadPart + (microseconds * freq.QuadPart) / 1000000;

    do {
        QueryPerformanceCounter(&end);
    } while (end.QuadPart < target);
}

void GetApplePos( char* Apple, char snake[ ][2], int Length )
{
	int i = 0;
	srand( time( NULL ) );
	while( i == 0 )
	{
		i = 1;
		Apple[ 0 ] = rand() % 79;
		Apple[ 1 ] = rand() % 27;
		for( int j = 0; j <= Length; j ++ )
		{
			if( Apple[ 0 ] == snake[ j ][ 0 ] && Apple[ 1 ] == snake[ j ][ 1 ] )
			{
				i = 0;
				break;
			}
		}
	}
}

int GameBegin( int *score, int *MaxScore )
{
	SpaceFrame();
	char snake[ 900 ][ 2 ] = { -1 };
	snake[ 0 ][ 0 ] = 15;
	snake[ 0 ][ 1 ] = 15;
	snake[ 1 ][ 0 ] = 14;
	snake[ 1 ][ 1 ] = 15;
	snake[ 2 ][ 0 ] = 13;
	snake[ 2 ][ 1 ] = 15;
	snake[ 3 ][ 0 ] = 12;
	snake[ 3 ][ 1 ] = 15;
	snake[ 4 ][ 0 ] = 11;
	snake[ 4 ][ 1 ] = 15;
	( *score ) = 0;
	int move = Rt;	//记录头运动方向，初始向右
	int Length = 4;
	int Eating = 1;
	int difficulty = 1;
	int ExtraScore = 0;
	char NextPos[ 2 ], LastPos[ 2 ];
	char Apple[ 2 ] = { -1, -1 };
	char ch;

	setCursorPos( 83, 0 );
	printf( "Current Score:" );
	setCursorPos( 83, 2 );
	printf( "Highest Score in history:" );
	setCursorPos( 83, 4 );
	printf( "Current Difficulty:" );
	
	while( 1 )
	{
		setCursorPos( 83, 1 );
		printf( "%d", *score );
		setCursorPos( 83, 3 );
		printf( "%d", *MaxScore );
		NextPos[ 0 ] = snake[ 0 ][ 0 ];
		NextPos[ 1 ] = snake[ 0 ][ 1 ];
		ExtraScore = difficulty - 1;
		if( ExtraScore > 8 )
		{
			ExtraScore = 8;
		}
		if( kbhit() )
		{
			ch = getch();
			switch( ch )
			{
				case 'w':
				{
					if( move == Lt || move == Rt )
						move = Up;
					break;
				}
				case 'W':
				{
					if( move == Lt || move == Rt )
						move = Up;
					break;
				}
				case 's':
				{
					if( move == Lt || move == Rt )
						move = Dn;
					break;
				}
				case 'S':
				{
					if( move == Lt || move == Rt )
						move = Dn;
					break;
				}
				case 'a':
				{
					if( move == Dn || move == Up )
						move = Lt;
					break;
				}
				case 'A':
				{
					if( move == Dn || move == Up )
						move = Lt;
					break;
				}
				case 'd':
				{
					if( move == Dn || move == Up )
						move = Rt;
					break;
				}
				case 'D':
				{
					if( move == Dn || move == Up )
						move = Rt;
					break;
				}
				case '1':
				{
					difficulty --;
					break;
				}
				case '2':
				{
					difficulty ++;
					break;
				}
				case 27:
				{
					return 0;
				}
			}
			if( ch == 0 || ch == 0xE0 )
			{
				ch = getch();
				switch( ch )
				{
					case 59:
					{
						difficulty --;
						break;
					}
					case 60:
					{
						difficulty ++;
						break;
					}
				}
			}
		}
		
		switch( move )
		{
			case Rt:
			{
				snake[ 0 ][ 0 ] += 1;
				break;
			}
			case Lt:
			{
				snake[ 0 ][ 0 ] -= 1;
				break;
			}
			case Up:
			{
				snake[ 0 ][ 1 ] -= 1;
				break;
			}
			case Dn:
			{
				snake[ 0 ][ 1 ] += 1;
				break;
			}
		}
		
		if( snake[ 0 ][ 0 ] == -1 || snake[ 0 ][ 0 ] == 80 || snake[ 0 ][ 1 ] == 28 || snake[ 0 ][ 1 ] == -1 )
		{
			return 1;
		}
		for( int i = 1; i <= Length; i ++ )
		{
			if( snake[ 0 ][ 0 ] == snake[ i ][ 0 ] && snake[ 0 ][ 1 ] == snake[ i ][ 1 ] )
			{
				return 2;
			}
		}
		if( Eating == 1 )
		{
			GetApplePos( Apple, snake, Length );
			setCursorPos( Apple[ 0 ], Apple[ 1 ] );
			printf( "\033[31m\u25CF\033[0m" );
			Eating = 0;
		}
		setCursorPos( NextPos[ 0 ], NextPos[ 1 ] );
		printf( " " );
		setCursorPos( snake[ 0 ][ 0 ], snake[ 0 ][ 1 ] );
		printf( "\033[33m\u25A1\033[0m" );
		for( int i = 1; i <= Length; i ++ )
		{
			setCursorPos( snake[ i ][ 0 ], snake[ i ][ 1 ] );
			printf( " " );
			LastPos[ 0 ] = snake[ i ][ 0 ];
			LastPos[ 1 ] = snake[ i ][ 1 ];
			snake[ i ][ 0 ] = NextPos[ 0 ];
			snake[ i ][ 1 ] = NextPos[ 1 ];
			NextPos[ 0 ] = LastPos[ 0 ];
			NextPos[ 1 ] = LastPos[ 1 ];
			setCursorPos( snake[ i ][ 0 ], snake[ i ][ 1 ] );
			printf( "*" );
		}
		if( snake[ 0 ][ 0 ] == Apple[ 0 ] && snake[ 0 ][ 1 ] == Apple[ 1 ] )
		{
			Length ++;
			snake[ Length ][ 0 ] = NextPos[ 0 ];
			snake[ Length ][ 1 ] = NextPos[ 1 ];
			Eating = 1;
			difficulty ++;
			( *score ) += 1 + ExtraScore;
		}

		if( difficulty < 1 )
		{
			difficulty = 1;
		}
		setCursorPos( 83, 5 );
		printf( "%d", difficulty );
		MiSleep( ( int )(( 0.7 + 6.5 / pow( (double)difficulty, 1.0/2.0 ) ) * 10000) );
		
	}

	return 0;
}

int GameOver( int over, int score, int MaxScore )
{
	SpaceFrame();
	char ch;
	switch( over )
	{
		case 0:
		{
			setCursorPos( 20, 9 );
			printf( "Game Over, you exit the game" );
			break;
		}
		case 1:
		{
			setCursorPos( 18, 9 );
			printf( "Game Over, you Bump into the wall" );
			break;
		}
		case 2:
		{
			setCursorPos( 20, 9 );
			printf( "Game Over, you bite yourself" );
			break;
		}
	}
	setCursorPos( 28, 11 );
	printf( "Your Score: %d", score );
	setCursorPos( 18, 13 );
	printf( "Your Highest Score in history: %d", MaxScore );
	if( score > MaxScore )
	{
		setCursorPos( 21, 15 );
		printf( "\033[32mYou have broken the Record!\033[0m" );
	}
	else
	{
		setCursorPos( 9, 15 );
		printf( "\033[31mYou are still %d point awayfrom breaking the record!\033[0m", MaxScore - score );
	}
	setCursorPos( 25, 18 );
	printf( "(1) Restar the Game" );
	setCursorPos( 25, 20 );
	printf( "(Esc) Exit the Game" );
	while( 1 )
	{
		if( kbhit() )
		{
			ch = getch();
			switch( ch )
			{
				case '1':
				{
					return 0;
				}
				case 27:
				{
					return 1;
				}	
			}
		}
	}
}

int Welcome( int MaxScore, FILE *fprecord )
{
	char ch;
	
Quit:
	SpaceFrame();
	setCursorPos( 28, 7 );
	printf( "     \033[31m\u25CF\033[0m       \033[33m\u25A1\033[0m********" );
	setCursorPos( 28, 8 );
	printf( "Game of Greedy Snake *" );
	setCursorPos( 28, 9 );
	printf( "   *******************" );
	setCursorPos( 28, 11 );
	printf( "(1) Start the Game" );
	setCursorPos( 28, 13 );
	printf( "(2) Check my Highest score" );
	setCursorPos( 28, 15 );
	printf( "(3) Check the Instructions" );
	setCursorPos( 28, 17 );
	printf( "(Esc) Exit the Game " );
	while( 1 )
	{
		if( kbhit() )
		{
			ch = getch();
			switch( ch )
			{
				case '3':
				{
					SpaceFrame();
					setCursorPos( 0, 0 );
					printf( "(1) Please turn to ENGLISH input method to play the game.\n" );
					printf( "(2) Press 'w, s, a, d', to control your snake.\n" );
					printf( "(3) The game will become harder when you eat frute.\n" );
					printf( "(4) The Difficulty can be turn up/down by press '1/2'.\n" );
					printf( "(5) You can get extra score by enhance the difficulty,\n" );
					printf( "    the extra score is equal to the difficulty, but less than 8.\n" );
					printf( "(6) The Snack can't turn round directly.\n" );
					printf( "(7) Snake Head: \033[33m\u25A1\033[0m,   snake body: *,   fruit: \033[31m\u25CF\033[0m,   frame: \u2588\u2588.\n\n\n" );
					printf( "\033[5mpress 'enter' to return\033[0m" );
					getchar();
					goto Quit;
				}
				case '2':
				{	
					SpaceFrame();
					setCursorPos( 18, 13 );
					printf( "Your Highest Score in history: %d", MaxScore );
					setCursorPos( 21, 16 );
					printf( "\033[5mpress 'enter' to return\033[0m" );
					getchar();
					goto Quit;
				}
				case '1':
				{
					return 1;
				}
				case 27:
				{		
					return 0;
				}
			}
		}
	}
	
}

int main( int argc, char *argv[] )
{
	if( argc < 2 )
	{
		printf( "%d", argc );
		return 0;
	}
	
	hideCursor();
	SetConsoleOutputCP( 65001 );
	FILE *fprecord = fopen( argv[ 1 ], "r+" );

	
	int score = 0;	        //分数，吃到果子的个数，总是长度-1
	int MaxScore = 0, num;
	int over = 0, choose;
	int restar = 0;
	char Rscore[ 20 ] = { 0 };
	long FileL;
	
	if ( fprecord == NULL )
	{
		printf( "wrong: %s\n", strerror(errno) );
		perror( "fopen failed" );
		return 0;
	}
	fseek( fprecord, 0L, SEEK_END );
	FileL = ftell( fprecord );
	fseek( fprecord, 0L, SEEK_SET );
	if( FileL == 0 )
	{
		fputc( 48, fprecord );
	}
	else
	{
		for( long i = 0; i < FileL; i ++ )
		{
			//fread( &num, 1, 1, fprecord );
			MaxScore *= 10;
			MaxScore += fgetc( fprecord ) - 48;
		}
	}
	
	while( restar == 0 )
	{
		choose = Welcome( MaxScore, fprecord );
		if( choose == 1 )
		{
			over = GameBegin( &score, &MaxScore );
			restar = GameOver( over, score, MaxScore );
			if( score > MaxScore )
			{
				MaxScore = score;
				int count = 0;
				int fd = fileno( fprecord );
				_chsize( fd, 0 );
				fseek( fprecord, 0L, SEEK_SET );
				while( score != 0 )
				{
					Rscore[ count ] = score % 10 + 48;
					score /= 10;
					count ++;
				}
				for( ; count - 1 >= 0; count -- )
				{
					fputc( Rscore[ count - 1 ], fprecord );
				}
			}
		}
		else
		{
			break;
		}
		
	}
	
	
	fclose( fprecord );
	
	ClearScreen();
	printf( "The program has finished" );
	
	return 0;
}